package edu.ucdavis.genomics.metabolomics.binbase.util;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Vector;

import org.apache.maven.plugin.logging.Log;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;

/**
 * used to find classes and packages
 * 
 * @author wohlgemuth
 */
public class PackageFinder {

	/**
	 * finds all classes used by this class
	 * 
	 * @param classFile
	 * @return
	 * @throws IOException
	 */
	public static List<String> findClasses(InputStream classFile, final Log log) throws IOException {
		final List<String> classes = new Vector<String>();

		ClassVisitor visitor = new ClassVisitor() {

			public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
				addClass(superName, classes);
			}

			public void visitSource(String source, String debug) {
			}

			public void visitOuterClass(String owner, String name, String desc) {
			}

			public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
				return null;
			}

			public void visitAttribute(Attribute attr) {
			}

			public void visitInnerClass(String name, String outerName, String innerName, int access) {
			}

			public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {

				if (desc.length() > 5) {
					if (desc.startsWith("L")) {
						desc = desc.substring(1, desc.length() - 1);
						addClass(desc, classes);
					}
				}
				return null;
			}

			public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
				// parsing arguments
				if (desc.indexOf(";") > 0) {

					String arguments = desc.substring(1, desc.lastIndexOf(")"));

					if (arguments.length() > 1) {
						String args[] = arguments.split(";");

						for (String arg : args) {
							if (arg.length() > 2) {
								int shift = 1;

								if (arg.charAt(0) == '[') {
									shift++;
								}
								arg = arg.substring(shift, arg.length());

								addClass(arg, classes);
							}
						}
					}

					// parsing returm types

					String type = desc.substring(desc.lastIndexOf(")") + 1);

					if (type.length() > 2) {
						int shift = 1;

						if (type.charAt(0) == '[') {
							shift++;
						}
						type = type.substring(shift, type.length() - 1);
						addClass(type, classes);
					}

					if (exceptions != null) {
						for (String e : exceptions) {
							addClass(e, classes);
						}
					}
				}
				return null;
			}

			public void visitEnd() {
			}

		};

		try {
			ClassReader reader = new ClassReader((classFile));
			reader.accept(visitor, true);
		}
		catch (NullPointerException e) {
			// log.warn(e.getMessage(),e);
		}
		return classes;
	}

	/**
	 * finds all packages used by this classFile
	 * 
	 * @param classFile
	 * @return
	 * @throws IOException
	 */
	public static List<String> findPackages(InputStream classFile, Log log) throws IOException {
		List<String> classes = findClasses(classFile, log);
		List<String> packages = new Vector<String>();

		for (String className : classes) {
			className = className.substring(0, className.lastIndexOf("."));
			addPackage(className, packages);
		}
		return packages;
	}

	/**
	 * returns all packages which match the filter
	 * 
	 * @param classFile
	 * @param filter
	 * @param log
	 * @return
	 * @throws IOException
	 */
	public static List<String> findFilterPackages(InputStream classFile, String filter, Log log) throws IOException {
		List<String> packages = findPackages(classFile, log);
		List<String> eclipsePackage = new Vector<String>();

		for (String packageName : packages) {
			if (packageName.indexOf(filter) > -1) {
				if (eclipsePackage.contains(packageName) == false) {
					eclipsePackage.add(packageName);
				}
			}
		}
		return eclipsePackage;
	}

	private static void addClass(String className, List<String> storage) {
		String name = className.replaceAll("/", ".");

		if (name.length() > 10) {
			if (storage.contains(name) == false) {
				storage.add(name);
			}
		}
	}

	private static void addPackage(String packageName, List<String> packages) {
		if (packages.contains(packageName) == false) {
			packages.add(packageName);
		}
	}
}
